#include "dexclip.h"
#include "ui_dexclip.h"

#ifdef DEBUG
#include <QDebug>
#endif

#include <QActionGroup>
#include <QCloseEvent>
#include <QCursor>
#include <QDataStream>
#include <QFile>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
#include <QTimer>

DexClip::DexClip(QWidget * parent)
	: QDialog(parent),
	  ui(new Ui::DexClip),
	  history(new QActionGroup(this)),
	  menu(new QMenu(this)),
	  iconTray(new QSystemTrayIcon(this)),
	  settings(new QSettings(QSettings::IniFormat, QSettings::UserScope, "Dexios", "DexClip", this)),
	  timer(new QTimer(this)),
	  startingUp(true)
	{
		ui->setupUi(this);
#ifndef Q_WS_X11
		ui->cbIgnoreSelections->deleteLater();
#endif
		clipboard = QApplication::clipboard();

//		connect(clipboard, SIGNAL(changed(QClipboard::Mode)),
//				this, SLOT(updateClipboardMenu(QClipboard::Mode)));

		// dataChanged() is more cross-platform friendly than changed(QClipboard::Mode)
		connect(clipboard, SIGNAL(dataChanged()),
				this, SLOT(updateMenuWithClipboard()));
		connect(clipboard, SIGNAL(selectionChanged()),
				this, SLOT(updateMenuWithSelection()));

		menu->setDefaultAction(menu->addSeparator());
		separator = menu->defaultAction();
		connect(menu->addAction(QIcon(":/icons/edit-clear.png"), tr("Clea&r History")), SIGNAL(triggered()),
				this, SLOT(clearClipboard()));
		connect(menu->addAction(QIcon(":/icons/configure.png"), tr("&Configure")), SIGNAL(triggered()),
				this, SLOT(show()));
		connect(menu->addAction(QIcon(":/icons/help-about.png"), tr("&About")), SIGNAL(triggered()),
				this, SLOT(about()));
		menu->addSeparator();
		connect(menu->addAction(QIcon(":/icons/application-exit.png"), tr("&Quit")), SIGNAL(triggered()),
				qApp, SLOT(quit()));

		init();

		iconTray->setContextMenu(menu);
		iconTray->setIcon(QIcon(":/icons/edit-paste.png"));
		iconTray->show();

#ifdef Q_WS_X11
		connect(iconTray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
				this, SLOT(systrayActivated(QSystemTrayIcon::ActivationReason)));
#endif

		connect(history, SIGNAL(triggered(QAction*)),
				this, SLOT(setClipboardText(QAction*)));

		connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)),
				this, SLOT(clicked(QAbstractButton*)));

		timer->setInterval(2000);
		timer->setSingleShot(true);
		connect(timer, SIGNAL(timeout()),
				this, SLOT(saveHistory()));
//		connect(timer, SIGNAL(timeout()),
//				timer, SLOT(stop()));
	}

DexClip::~DexClip()
	{
#ifdef DEBUG
		qDebug() << "Closing ...";
#endif
		delete ui;

		saveHistory();
	}

void DexClip::about()
	{
		QMessageBox::about(this,
						   tr("DexClip"),
						   tr("<h2>Dexios Clipboard Manager</h2>"
							  "<p>Lightweight, cross-platform clipboard manager, "
							  "part of the Dexios desktop environment.</p>"
							  "<p><a href=\"http://code.google.com/p/dexiosdesktop/\">Dexios DE</a></p>"));
	}

void DexClip::changeEvent(QEvent * e)
	{
		QDialog::changeEvent(e);
		switch (e->type())
		{
			case QEvent::LanguageChange:
				ui->retranslateUi(this);
				break;
			default:
				break;
		}
	}

void DexClip::clearClipboard()
	{
#ifdef DEBUG
		qDebug() << "Clearing clipboard history ...";
#endif
		clipboard->clear(QClipboard::Clipboard);
		if (clipboard->supportsSelection())
			clipboard->clear(QClipboard::Selection);

		foreach (QAction *action, history->actions())
			action->deleteLater();

		menu->setDefaultAction(separator);
		if (ui->gbSaveNewEntries->isChecked()
			&& !ui->cbOnlyOnExit->isChecked())
				timer->start();
	}

void DexClip::clicked(QAbstractButton * button)
	{
		switch (ui->buttonBox->standardButton(button))
		{
			case QDialogButtonBox::Save:
				saveSettings();
			case QDialogButtonBox::Close:
				hide();
				break;
		}
	}

inline void DexClip::init()
	{
		restoreSettings();
		restoreHistory();
	}

void DexClip::restoreHistory()
	{
		QFile file(DexClip::homePath+"/history.dat");
		if (file.open(QIODevice::ReadOnly))
		{
#ifdef DEBUG
			qDebug() << "Restoring history ...";
#endif
			QDataStream in(&file);
			QStringList data;
			in >> data;
			foreach (QString str, data)
				clipboard->setText(str, QClipboard::Clipboard);

			file.close();
		}
	}

void DexClip::restoreSettings()
	{
		settings->beginGroup("Session");
			ui->sbMaxEntries->setValue(settings->value("MaxEntries", 10).toInt());
			ui->sbMaxChars->setValue(settings->value("MaxChars", 25).toInt());
#ifdef Q_WS_X11
			ui->cbIgnoreSelections->setChecked(settings->value("IgnoreSelections", true).toBool());
#endif
			ui->gbSaveNewEntries->setChecked(settings->value("AutoSaveHistory", true).toBool());
			ui->cbOnlyOnExit->setChecked(settings->value("OnlyOnExit", false).toBool());
		settings->endGroup();
	}

void DexClip::saveHistory()
	{
		if (startingUp)
		{
			startingUp = false;
			return;
		}

		QFile file(DexClip::homePath+"/history.dat");
		if (file.open(QIODevice::WriteOnly
					  | QIODevice::Truncate))
		{
#ifdef DEBUG
			qDebug() << "Saving history...";
#endif
			QDataStream out(&file);
			QStringList data;
			foreach (QAction * action, history->actions())
				data << action->data().toString();
			out << data;
			file.close();
		}
	}

void DexClip::saveSettings()
	{
		settings->beginGroup("Session");
			settings->setValue("MaxEntries", ui->sbMaxEntries->value());
			settings->setValue("MaxChars", ui->sbMaxChars->value());
#ifdef Q_WS_X11
			settings->setValue("IgnoreSelections", ui->cbIgnoreSelections->isChecked());
#endif
			settings->setValue("AutoSaveHistory", ui->gbSaveNewEntries->isChecked());
			if (ui->gbSaveNewEntries->isChecked())
				settings->setValue("OnlyOnExit", ui->cbOnlyOnExit->isChecked());
			else
				settings->setValue("OnlyOnExit", false);
		settings->endGroup();
	}

void DexClip::setClipboardText(QAction * action)
	{
		if (action != menu->defaultAction())
		{
			menu->insertAction(menu->defaultAction(), action);
			menu->setDefaultAction(action);
		}
		clipboard->setText(action->data().toString(), QClipboard::Clipboard);
	}

void DexClip::systrayActivated(QSystemTrayIcon::ActivationReason reason)
	{
		switch (reason)
		{
			case QSystemTrayIcon::Trigger:
				menu->exec(QCursor::pos());
				break;
			case QSystemTrayIcon::Context:
			case QSystemTrayIcon::DoubleClick:
			case QSystemTrayIcon::MiddleClick:
			case QSystemTrayIcon::Unknown:
				break;
		}
	}

void DexClip::updateClipboardMenu(QClipboard::Mode mode)
	{
		QString text = clipboard->text(mode);
		QAction *a = NULL;
		switch (mode)
		{
#ifdef Q_WS_X11
			 case QClipboard::Selection:
#ifdef DEBUG
				qDebug() << "Clipboard has updated (selection) ...";
#endif
				if (ui->cbIgnoreSelections->isChecked())
					return;
#endif
			 case QClipboard::Clipboard:
#ifdef DEBUG
				qDebug() << "Clipboard has updated (clipboard) ...";
#endif
				// Prevent duplicate entries
				foreach (QAction *action, history->actions())
				{
					if (text == action->data().toString())
					{
						if (action != menu->defaultAction())
						{
							menu->insertAction(menu->defaultAction(), action);
							menu->setDefaultAction(action);
						}
						return;
					}
				}

				a = history->addAction(text);
				// Store text as action data to not interefere with action text
				a->setData(text);
				// Then shrink the action text if it is too big
				if (text.length() > ui->sbMaxChars->value())
				{
					text.resize(ui->sbMaxChars->value());
					a->setText(text+" ...");
				}

				// Bring new entry to the top
				menu->insertAction(menu->defaultAction(), a);
				menu->setDefaultAction(a);

				// Remove entries greater than max entries
				if (history->actions().count() > ui->sbMaxEntries->value())
				{
#ifdef DEBUG
					qDebug() << "   More than"
							 << ui->sbMaxEntries->value()
							 << "max entries ...";
#endif
					if (menu->defaultAction() != history->actions().first())
						history->actions().first()->deleteLater();

					else
					{
						int i = history->actions().indexOf(menu->defaultAction());
						--i;
						history->actions().at(i)->deleteLater();
					}
				}

#ifdef DEBUG
				else
					qDebug() << "   Not greater than ...";
#endif

				if (ui->gbSaveNewEntries->isChecked()
					&& !ui->cbOnlyOnExit->isChecked())
						timer->start();
				break;

			case QClipboard::FindBuffer:
				break;
		}
	}

void DexClip::updateMenuWithClipboard()
	{
		updateClipboardMenu(QClipboard::Clipboard);
	}

void DexClip::updateMenuWithSelection()
	{
		updateClipboardMenu(QClipboard::Selection);
	}
